

/*
--如何解析  Frame
  - Simple String : "+OK\r\n"
  - error: "-Error message\r\n"
  - Bulk errors: "!<length>\r\n<error>\r\n"
  - integers: ":[<+|->]<value>\r\n"
  - bulk String: "$<length>\r\n<data>\r\n"
  - null bulk string : "$-1\r\n"
  - array : "*<number-of-elements>\r\n<element-1>...<element-n>"
  - null array: "*-1\r\n"
  - null: "_\r\n"
  - boolean: "#<t|f>\r\n"
  - double: ",[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]\r\n"
  - Big numbers: "([+|-]<number>\r\n"  

  - map: "%<number-of-entries>\r\n<key-1><value-1>...<key-n><value-n>"
  - set: "~<number-of-elements>\r\n<element-1>...<element-n>"
*/

use crate::resp::{BulkString, RespArray, RespEncode, RespMap, RespNull, RespNullArray, RespNullBulkString, RespSet, SimpleError, SimpleString};


const BUF_CAP:usize = 4096;

impl RespEncode for SimpleString {
    fn encode(self) -> Vec<u8> {
        format!("+{}\r\n",self.0).into_bytes()
    }
}

impl RespEncode for SimpleError {
    fn encode(self) -> Vec<u8> {
        format!("-{}\r\n",self.0).into_bytes()
    }
}

impl RespEncode for i64 {
    fn encode(self) -> Vec<u8> {
        let sign = if self < 0 {""} else {"+"};
        format!(":{}{}\r\n", sign,self).into_bytes()
    }
}

impl RespEncode for BulkString {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(self.len() + 16);
        buf.extend_from_slice(&format!("${}\r\n",self.len()).into_bytes());
        buf.extend_from_slice(&self);
        buf.extend_from_slice(b"\r\n");
        buf

        // 这个不可以一下写完吗？
    }
}

impl RespEncode for RespNullBulkString{
    fn encode(self) -> Vec<u8> {
        b"$-1\r\n".to_vec()
    }
}

// - null array: "*-1\r\n"
impl RespEncode for RespNullArray {
    fn encode(self) -> Vec<u8> {
        b"*-1\r\n".to_vec()
    }
}

impl RespEncode for RespNull{
    fn encode(self) -> Vec<u8> {
        b"_\r\n".to_vec()
    }
}

impl RespEncode for RespArray{
    fn encode(self) -> Vec<u8> {
        let  mut buf = Vec::with_capacity(BUF_CAP);
        buf.extend_from_slice(&format!("*{}\r\n",self.0.len()).into_bytes());
        for frame in self.0 {
            buf.extend_from_slice(&frame.encode());
        }

        buf
    }
}

impl RespEncode for bool {
    fn encode(self) -> Vec<u8> {
        format!("#{}\r\n", if self {"t"} else {"f"}).into_bytes()
    }
}

impl RespEncode for f64 {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(32);
        let ret = if self.abs() > 1e+8 {
            format!(",{:+e}\r\n",self)
        }else {
            let sign = if self < 0.0 {""} else {"+"};
            format!(",{}{}\r\n", sign, self)
        };


        buf.extend_from_slice(&ret.into_bytes());
        buf
    }

    
}


impl RespEncode for RespMap {
    fn encode(self) -> Vec<u8> {

        let mut buf = Vec::with_capacity(BUF_CAP);
        buf.extend_from_slice(&format!("%{}\r\n",self.len()).into_bytes()); 
        for (key,value) in self.0 {
            buf.extend_from_slice(&SimpleString(key).encode());
            buf.extend_from_slice(&value.encode());
        }
        buf

    }
}


impl RespEncode  for RespSet{
    fn encode(self) -> Vec<u8> {

        let mut buf = Vec::with_capacity(BUF_CAP);

        buf.extend_from_slice(&format!("~{}\r\n",self.len()).into_bytes());
        for frame in self.0 {
            buf.extend_from_slice(&frame.encode());
        }
        buf

    }
}



#[cfg(test)]
mod tests{
    use crate::resp::RespFrame;

    use super::*;

    #[test]
    fn test_string_encode(){
        let frame: RespFrame = SimpleString::new("OK".to_string()).into();

        assert_eq!(frame.encode(), b"+OK\r\n");
    }

    #[test]
    fn test_error_encode(){
        let frame: RespFrame = SimpleError::new("Error message".to_string()).into();

        assert_eq!(frame.encode(), b"-Error message\r\n");
    }
    
    #[test]
    fn test_integer_encode(){
        let frame : RespFrame = 123.into();
        assert_eq!(frame.encode(), b":+123\r\n");

        let frame : RespFrame = (-123).into();
        assert_eq!(frame.encode(), b":-123\r\n");
    }

    #[test]
    fn test_bulk_string_encode(){
        let frame: RespFrame = BulkString::new(b"hello".to_vec()).into();
        assert_eq!(frame.encode(), b"$5\r\nhello\r\n");
    }

    //   - null bulk string : "$-1\r\n"
    #[test]
    fn test_null_bulk_string_encode(){
        let frame: RespFrame = RespNullBulkString.into();
        assert_eq!(frame.encode(), b"$-1\r\n");
    }


    // - array : "*<number-of-elements>\r\n<element-1>...<element-n>"
    // simple string: b"+OK\r\n"
    #[test]
    fn test_array_encode(){
        let frame: RespFrame = RespArray::new(vec![
            SimpleString::new("set".to_string()).into(),
            SimpleString::new("hello".to_string()).into(),
            SimpleString::new("world".to_string()).into(),
            123.into(),
            (-10789).into(),
        ])
        .into();
        assert_eq!(frame.encode(), b"*5\r\n+set\r\n+hello\r\n+world\r\n:+123\r\n:-10789\r\n");
    }

    #[test]
    fn test_null_array_encode(){
        let frame: RespFrame = RespNullArray.into();
        assert_eq!(frame.encode(), b"*-1\r\n");
    }

    #[test]
    fn test_null_encode(){
        let frame: RespFrame = RespNull.into();
        assert_eq!(frame.encode(), b"_\r\n");
    }

    #[test]
    fn test_boolean_encode(){
        let frame: RespFrame = true.into();
        assert_eq!(frame.encode(), b"#t\r\n");

        let frame: RespFrame = false.into();
        assert_eq!(frame.encode(), b"#f\r\n");
    }


    // - double: ",[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]\r\n"
    #[test]
    fn test_double_encode(){
        let frame: RespFrame = 123.456.into();
        assert_eq!(frame.encode(), b",+123.456\r\n");

        let frame: RespFrame = (-123.456).into();
        assert_eq!(frame.encode(), b",-123.456\r\n");

        // 带e 的有问题
        let frame: RespFrame = 1.23456e+8.into();
        assert_eq!(frame.encode(), b",+1.23456e8\r\n");

        let frame: RespFrame = 1.23456e-8.into();
        assert_eq!(frame.encode(), b",+1.23456e-8\r\n");

        
        let frame: RespFrame = (-1.23456e+8).into();
        assert_eq!(frame.encode(), b",-1.23456e+8\r\n");

        let frame: RespFrame = (-1.23456e-8).into();
        assert_eq!(frame.encode(), b",-1.23456e-8\r\n");
    }


    // #[test]
    // fn test_set_encode(){
    //     let frame: RespFrame = RespSet::new([

    //     ])
    //     .into();
    // }

}